关于@property中NSString的copy-深入细节

前几天和东盟君讨论关于在 @property(nonatomic,copy)NSString * string;copy的使用问题. 虽然自己很明白,见到NSString,果断用 copy,但是却说不出个道道.

今天偶尔在一片国外论坛的文章上看到了关于这个的内容(因为在pad上看的,具体地址没法引用了).所以简单记录一下.

因为类似 NSString这样的不可变父类,它们有可变的子类(比如 NSMutableString),那么就可能造成修改指针指向的结果,而这是我们不愿意看到的

是比较抽象的描述,要弄明白这段话,需要明确 copy 做了什么 ?

在官方的描述中: copy是复制一个对象,并强引用它.

那么开始解释一开始的话:

假设我有一个 Person 类,他有一个 name属性,它是NSString类型的.不可变对象对吧.

那么问题来了,下面的语法是成立的:

1
2
3
 NSMutableString * mutableString = [[NSMutableString alloc]initWithString:@"gg"];
//1
per.name = mutableString;

上面1处首先语法没有问题, NSString是 NSMutableString的父类.
然后这个时候,如果 name属性是 @propertystrong的,
那么 per.namemutableString 指向的是同一块内存区域,
那么,我就可以通过修改 mutableString来修改per.name了,而当初我们既然选择了 NSString,那么就是考虑到不会对其进行修改的.所以,这样违背了我们的初衷.

如果是copy,per.name在使用前会复制一份出来,这样使用的其实是它的副本,即使修改了mutableString也不会对per.name的本尊造成影响.

关于 copy 和 mutableCopy

现在我说的这两者是在代码中使用的时候,就是对象创建和赋值时候的.
苹果的设计是:

  • copy 拿到的永远是不可变对象
  • mutableCopy 拿到的是可变对象

为什么这样设计呢?

以上两者的调用者可能是 可变的或者不可变的,那么就会有四种组合,对于开发者去记忆四种组合是比较蹩脚的.所以干脆

  • 不管调用者类型是可变与否,copy到得就不可变
  • 不管调用者类型是可变与否,mutableCopy到得就是可变对象

关于创建可变类型

以前都是用类似 NSMutableString * string = [ NSMutableString string ];这样的方式创建,但是在和东盟君讨论的时候,打印了一下,发现 string的class是:

1
2
3
4
5
NSString * string = [NSString stringWithFormat:@"string"];
NSLog(@"string is %@",[string class]);

NSMutableString * mutableString = [NSMutableString string];
NSLog(@"mutable string is %@",[mutableString class]);

打印结果:

1
2
2015-05-20 10:36:43.549 TestNsstring[10993:1874316] string is __NSCFString
2015-05-20 10:36:43.550 TestNsstring[10993:1874316] mutable string is __NSCFString

究其原因, NSString是一个类簇,具体的实现都会找到合适的类,这个不用纠结了.

不久之后,在 iOS6 by Tutorial中一书中看到关于 [@[] mutableCopy][@{} mutableCopy] 这样的用法.

也符合 copy 和 mutableCopy 的准则